package com.marion.jpa.service.impl;

import com.marion.jpa.common.criteria.Criteria;
import com.marion.jpa.common.criteria.Restrictions;
import com.marion.jpa.common.criteria.dynamic02.Filter01;
import com.marion.jpa.common.criteria.dynamic02.QueryParams01;

import com.marion.jpa.entity.TUserEntity;
import com.marion.jpa.query.UserSearchQuery;
import com.marion.jpa.query.UserSearchQuery002;
import com.marion.jpa.repo.UserRepo;
import com.marion.jpa.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.ArrayList;
import java.util.List;

/**
 * @author Marion
 * @date 2022/3/2 14:52
 */
@Service
public class UserServiceImpl implements UserService {

    private final UserRepo userRepo;

    @Autowired
    public UserServiceImpl(UserRepo userRepo) {
        this.userRepo = userRepo;
    }

    @Override
    public TUserEntity findById(long id) {
        return userRepo.findById(id).orElse(null);
    }

    @Override
    public List<TUserEntity> findAllByPage() {
        return userRepo.findAll();
    }

    @Override
    public List<TUserEntity> searchUser(UserSearchQuery searchQuery) {

        final UserSearchQuery userSearchQuery = searchQuery;

        // where name = x and gender = x
        Specification<TUserEntity> specification = new Specification<>() {

            /**
             * Creates a WHERE clause for a query of the referenced entity in form of a {@link Predicate} for the given
             * {@link Root} and {@link CriteriaQuery}.
             * @param root            must not be {@literal null}.
             * @param query           must not be {@literal null}.
             * @param criteriaBuilder must not be {@literal null}.
             * @return a {@link Predicate}, may be {@literal null}.
             */
            @Override
            public Predicate toPredicate(Root<TUserEntity> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {

                List<Predicate> predicates = new ArrayList<>();

                if (userSearchQuery.getName() != null) {
                    Predicate name = criteriaBuilder.equal(root.get("name"), userSearchQuery.getName());
                    predicates.add(name);
                }

                if (userSearchQuery.getGender() != null) {
                    Predicate gender = criteriaBuilder.equal(root.get("gender"), userSearchQuery.getGender());
                    predicates.add(gender);
                }

                List<Integer> ages = List.of(1, 2);
                CriteriaBuilder.In<Object> in = criteriaBuilder.in(root.get("age"));
                for (Integer age : ages) {
                    in.value(age);
                }

                predicates.add(criteriaBuilder.and(in));

                return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
            }
        };

        return userRepo.findAll(specification);
    }

    @Override
    public List<TUserEntity> searchUser001(UserSearchQuery query) {

        Criteria<TUserEntity> criteria = new Criteria<>();
        criteria.add(Restrictions.eq("gender", query.getGender()))
                .add(Restrictions.eq("name", query.getName()))
                .add(Restrictions.in("age", List.of(1, 2), false));

        return userRepo.findAll(criteria);
    }

    @Override
    public List<TUserEntity> searchUser002(UserSearchQuery002 query) {
        return userRepo.findAll(query.toSpec());
    }

    @Override
    public List<TUserEntity> searchUser003(UserSearchQuery query) {

        QueryParams01<TUserEntity> params = new QueryParams01<>();

        QueryParams01<TUserEntity> and = params.and(Filter01.eq("gender", query.getGender()),
                Filter01.eq("name", query.getName()));

        return userRepo.findAll(and);
    }

    @Override
    public List<TUserEntity> searchUser004(UserSearchQuery query) {
        return userRepo.findAllOrgByNameAndGender(query.getName(), query.getGender());
    }

    @Override
    public List<TUserEntity> searchUser005(UserSearchQuery query) {
        return null;
    }

}
